package com.metis.document.parse.dialog.controller;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.metis.document.parse.dialog.api.ParseControllerApi;
import com.metis.document.parse.dialog.api.PostProcessControllerApi;
import com.metis.document.parse.dialog.constant.FileStatus;
import com.metis.document.parse.dialog.entity.TaskPO;
import com.metis.document.parse.dialog.models.dto.ParseResultDTO;
import com.metis.document.parse.dialog.models.qo.CreateTaskQo;
import com.metis.document.parse.dialog.pipeline.TextPostProcess;
import com.metis.document.parse.dialog.pipeline.TitleTextPostProcess;
import com.metis.document.parse.dialog.repo.TaskRepo;
import com.metis.document.parse.dialog.entity.ParseParameter;
import com.metis.document.parse.dialog.service.ParseService;
import com.metis.document.parse.dialog.service.message_listener.ParseTaskMessageListener;
import com.metis.document.parse.dialog.utils.ByteArrayMultipartFile;
import com.metis.document.parse.dialog.utils.MessageDigestInputStream;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.Record;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
public class DialogController {
    @Autowired
    private TaskRepo taskRepo;
    @Autowired
    private RedisTemplate<String, String> redisStringTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private ParseControllerApi parseControllerApi;
    @Autowired
    private ParseService parseService;


    @Autowired
    private List<TextPostProcess> processes = new ArrayList<>();
    @Autowired
    private PostProcessControllerApi postProcessControllerApi;

    /**
     * parseJsonFile
     * @param file file
     * @param response response
     * @throws Exception exp
     */
    @ApiOperation("解析文件")
    @PostMapping(value = "/parse/parseJsonFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public void parseJsonFile(
            @ApiParam(value = "待解析文件")
            @RequestPart("file") MultipartFile file, HttpServletResponse response) throws Exception {

        ContentDisposition contentDisposition = ContentDisposition.builder("attachment")
                .filename(URLEncoder.encode("response.zip", StandardCharsets.UTF_8)).build();
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
        response.setHeader(HttpHeaders.CONTENT_TYPE, "application/zip");

        String json = getJson(file);
        ParseResultDTO parseResultDTO = JSONObject.parseObject(json, ParseResultDTO.class);
        parseService.parsePdfDTO(parseResultDTO, response.getOutputStream());
    }

    /**
     * 下载引擎解析文件
     * @param file file
     * @param response response
     * @throws IOException ex
     */
    @PostMapping(value = "/parse/parseMiddleFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public void parseMiddleFile(@RequestPart("file") MultipartFile file, HttpServletResponse response) throws IOException {
        ResponseEntity<ParseResultDTO> responseEntity = parseControllerApi.parsePDF(file, true, true, true, true, true, true, Collections.emptyList());
        ParseResultDTO parseResultDTO = responseEntity.getBody();
        String info = JSONObject.toJSONString(parseResultDTO);

        ContentDisposition contentDisposition = ContentDisposition.builder("attachment")
                .filename(URLEncoder.encode(file.getOriginalFilename()+".json", StandardCharsets.UTF_8)).build();
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
        response.setContentType("application/multipart/form-data");

        OutputStream os = response.getOutputStream();
        os.write(info.getBytes());
        os.flush();
        os.close();
    }

    @PostMapping("/parseText")
    public String parseText(@RequestBody String text){
        for (TextPostProcess textPostProcess : processes) {
            text = textPostProcess.process(text);
        }

        try {
            JSONObject json = new JSONObject();
            json.put("origin_title", text.substring(0,10));
            json.put("section_content", text);
            json.put("section_title","");
            json.put("section_id",1);
            json.put("rate", "1");
            json.put("title_level","");
            JSONArray arr = new JSONArray();
            arr.add(json);

            Map<String, Object> jsonData = new HashMap<>();
            jsonData.put("data_type","");
            jsonData.put("id",0);
            jsonData.put("title","");
            jsonData.put("content", arr);

            ResponseEntity<String> re = postProcessControllerApi.processDocument(jsonData);
            if(re.getStatusCode() == HttpStatus.OK){
                text = re.getBody();
            }
        }catch (Exception e){
            log.error(e.getMessage());
        }

        return text;
    }

    @PostMapping("/deDuplicates")
    public void deDuplicates(@RequestBody CreateTaskQo createTaskQo) {
        log.info("deDuplicates:{}", createTaskQo);
        File rootPath = new File(createTaskQo.getRootPath());
        File ignorePath = new File(createTaskQo.getIgnorePath());
        CompletableFuture.runAsync(() -> deDuplicates(rootPath, ignorePath, new HashSet<>()))
                .thenAccept(aVoid -> log.info("去重完成"))
                .exceptionally(throwable -> {
                    log.error(throwable.getMessage(), throwable);
                    return null;
                });
    }

    @ApiOperation("解析文件")
    @PostMapping(value = "/parse/parseFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public void parseFile(
            @ApiParam(value = "待解析文件")
            @RequestPart("file") MultipartFile file, HttpServletResponse response) throws Exception {

        ContentDisposition contentDisposition = ContentDisposition.builder("attachment")
                .filename(URLEncoder.encode("response.zip", StandardCharsets.UTF_8)).build();
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
        response.setHeader(HttpHeaders.CONTENT_TYPE, "application/zip");
        try (InputStream is = file.getInputStream();ByteArrayMultipartFile byteArrayMultipartFile = new ByteArrayMultipartFile(file.getName(), file.getOriginalFilename(), file.getContentType())) {
            IOUtils.copy(is, byteArrayMultipartFile);
            String fileName = file.getOriginalFilename();
            assert fileName != null;
            String fileExt = fileName.substring(fileName.lastIndexOf('.') + 1);
            ParseParameter parameter = new ParseParameter(byteArrayMultipartFile, response.getOutputStream(), fileName, fileExt);

            //parseService.parsePDF(byteArrayMultipartFile, response.getOutputStream());
            parseService.parsePDF(parameter);
        }
    }

    // 读取MultipartFile文件内容  content就是文件内容
    public String getJson(MultipartFile file) {
        String content = "";
        try {
            byte[] data = file.getBytes();
            content = new String(data, StandardCharsets.UTF_8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }



    @PostMapping("/executeTask")
    public void executeTask(@RequestBody CreateTaskQo createTaskQo) {
        log.info("createTaskQo:{}", createTaskQo);
        File rootPath = new File(createTaskQo.getRootPath());
        File storePath = new File(createTaskQo.getStorePath());
        File ignorePath = new File(createTaskQo.getIgnorePath());
        CompletableFuture.runAsync(() -> submitTask(rootPath, storePath, ignorePath))
                .thenAccept(aVoid -> log.info("任务提交完成"))
                .exceptionally(throwable -> {
                    log.error(throwable.getMessage(), throwable);
                    return null;
                });

    }

    @SneakyThrows
    private synchronized void deDuplicates(File file, File ignoreFilePath, Set<String> existSet) {
        if (!file.isDirectory() && file.exists()) {
            String checksum = countChecksum(file);
            if (existSet.contains(checksum)
                    || !taskRepo.findByChecksum(checksum).isEmpty()) {
                log.info("文件重复, path:{}", file.getAbsolutePath());
                FileUtils.moveFile(file, ignoreFilePath);
            } else {
                existSet.add(checksum);
            }
        } else {
            //遍历根目录下的所有文件
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File f : files) {
                deDuplicates(f, new File(ignoreFilePath, f.getName()), existSet);
            }
        }
    }

    @SneakyThrows
    private synchronized void submitTask(File file, File resultStorePath, File ignoreFilePath) {
        if (!file.isDirectory() && file.exists()) {
            long snowflakeId = IdUtil.getSnowflakeNextId();
            String filename = file.getName();
            String baseName = FilenameUtils.getBaseName(filename);
            String storeFileName = baseName + ".md";
            File newFile = new File(resultStorePath.getParentFile(), storeFileName);
            String checksum = countChecksum(file);
            TaskPO taskPO = TaskPO.builder()
                    .id(snowflakeId)
                    .filename(file.getName())
                    .filepath(file.getAbsolutePath())
                    .checksum(checksum)
                    .result_filepath(newFile.getAbsolutePath())
                    .filesize(Files.size(file.toPath()))
                    .status(FileStatus.WAITING)
                    .build();
            if (Files.exists(Path.of(newFile.getAbsolutePath()))) {
                return;
            }
            if (!taskRepo.findByChecksum(checksum).isEmpty()) {
                //如果处理过则直接忽略
                taskPO.setStatus(FileStatus.PARSE_SKIP);
                taskRepo.save(taskPO);
                FileUtils.moveFile(file, ignoreFilePath);
                return;
            }
            taskRepo.save(taskPO);
            //发送消息
            ParseTaskMessageListener.Message message = new ParseTaskMessageListener.Message();
            message.setTaskId(snowflakeId);
            message.setFilename(file.getName());
            message.setFilepath(file.getAbsolutePath());
            message.setResultFilepath(newFile.getAbsolutePath());
            message.setFilesize(Files.size(file.toPath()));
            String messageJson = objectMapper.writeValueAsString(message);
            redisStringTemplate.opsForStream().add(Record.of(messageJson).withStreamKey(ParseTaskMessageListener.STREAM_KEY));
            log.info("创建任务, path:{}", file.getAbsolutePath());
        } else {
            //遍历根目录下的所有文件
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File f : files) {
                try{
                    submitTask(f, new File(resultStorePath, f.getName()), new File(ignoreFilePath, f.getName()));
                }catch (Exception e){
                    log.error("遍历文件错误:",f.getName());
                }

            }
        }
    }

    public String countChecksum(@NotNull File file) throws IOException {
        try (MessageDigestInputStream mis = new MessageDigestInputStream(new BufferedInputStream(new FileInputStream(file)))) {
            IOUtils.copy(mis, NullOutputStream.NULL_OUTPUT_STREAM);
            return mis.getChecksum();
        }
    }
}
